package gamelogic

import (
	"math"
	"server_cluster/common/config"
	"server_cluster/common/gdconf"
	"server_cluster/common/logger"
	"server_cluster/common/mysock"
	"server_cluster/common/protoc/pb"
	"server_cluster/logic/sharemem"
	"slices"
)

func AddWeapon(itemId int32, user *sharemem.User) bool {
	var weapon = user.GetEmptyWeapon()
	if weapon == nil {
		logger.Error("no empty weapon found, uid: %v", user.GetUid())
		return false
	}
	weapon.WeaponKey = uint64(sharemem.SnowflakeWorker.GenId())
	weapon.ItemId = itemId
	weapon.Level = 1
	weapon.Exp = 0
	weapon.Promote = 1
	weapon.Star = 0
	weapon.RoleId = 0
	weapon.IsLock = false
	// 添加索引
	user.AddWeapon2Map(weapon.WeaponKey, weapon)
	user.SetLastComposeWeaponKey(weapon.WeaponKey)
	SendMsg(config.SC_WeaponUpdateNtf, &pb.SC_WeaponUpdateNtf{Weapon: user.PacketWeapon(weapon)}, nil, user)
	return true
}

func CostWeapon(weapon *sharemem.Weapon, user *sharemem.User) bool {
	weaponKey := weapon.WeaponKey
	if weapon.RoleId != 0 {
		logger.Error("weapon already be wear, weaponKey: %v, roleId: %v, uid: %v", weaponKey, weapon.RoleId, user.GetUid())
		return false
	}
	user.RemoveWeapon(weapon)
	SendMsg(config.SC_WeaponDeleteNtf, &pb.SC_WeaponDeleteNtf{DeleteWeaponKeyList: []uint64{weaponKey}}, nil, user)
	return true
}

func WeaponWear(weapon *sharemem.Weapon, role *sharemem.Role, isWear bool, user *sharemem.User) int16 {
	if isWear {
		// 穿戴
		if weapon.RoleId != 0 {
			// 该武器当前有其他角色穿戴
			var oldRole = user.GetRoleById(weapon.RoleId)
			if oldRole == nil {
				logger.Error("get oldRole is nil, roleId: %v, uid: %v", weapon.RoleId, user.GetUid())
				return config.OBJECT_NOT_FOUND
			}
			oldRole.Weapon = 0
			oldRole.SetSync()
			weapon.RoleId = 0
			SendMsg(config.SC_RoleUpdateNtf, &pb.SC_RoleUpdateNtf{Role: user.PacketUserRole(oldRole)}, nil, user)
		}
		if role.Weapon != 0 {
			// 该角色该部位当前有穿戴其他武器
			var oldWeapon = user.GetUserRoleWeaponByPtr(role)
			if oldWeapon == nil {
				logger.Error("get oldWeapon is nil, WeaponKey: %v", role.Weapon)
				return config.OBJECT_NOT_FOUND
			}
			oldWeapon.RoleId = 0
			SendMsg(config.SC_WeaponUpdateNtf, &pb.SC_WeaponUpdateNtf{Weapon: user.PacketWeapon(oldWeapon)}, nil, user)
		}
		role.Weapon = weapon.WeaponKey
		weapon.RoleId = role.GetId()
	} else {
		// 脱下
		if weapon.RoleId == 0 {
			logger.Error("weapon not be wear, WeaponKey: %v, uid: %v", weapon.WeaponKey, user.GetUid())
			return config.SERVER_ERROR
		}
		role.Weapon = 0
		weapon.RoleId = 0
	}
	role.SetSync()
	SendMsg(config.SC_RoleUpdateNtf, &pb.SC_RoleUpdateNtf{Role: user.PacketUserRole(role)}, nil, user)
	SendMsg(config.SC_WeaponUpdateNtf, &pb.SC_WeaponUpdateNtf{Weapon: user.PacketWeapon(weapon)}, nil, user)
	TriggerQuest(user, sharemem.QUEST_FINISH_COND_TYPE_WEAR_WEAPON, true)
	return config.OK
}

func WeaponUpgrade(weapon *sharemem.Weapon, req *pb.CS_WeaponUpgradeReq, user *sharemem.User) int16 {
	weaponKey := weapon.WeaponKey
	logger.Debug("weapon upgrade, weaponKey: %v, uid: %v", weaponKey, user.GetUid())
	// 检查配置
	weaponConfig := gdconf.GetWeaponConfig(weapon.ItemId)
	if weaponConfig == nil {
		return config.CONFIG_NOT_FOUND
	}
	weaponPromoteConfig := gdconf.GetWeaponPromoteConfigByGroupAndPromote(weaponConfig.PromoteGroup, int32(weapon.Promote))
	if weaponPromoteConfig == nil {
		return config.CONFIG_NOT_FOUND
	}
	// 武器等级是否达到限制
	if weapon.Level >= int16(weaponPromoteConfig.LevelLimit) {
		logger.Error("weapon level < level limit, level: %v, uid: %v", weapon.Level, user.GetUid())
		return config.SERVER_ERROR
	}
	// 是否达到最大等级
	weaponLevelConfig := gdconf.GetWeaponLevelConfigByGroupAndLevel(weaponConfig.LevelGroup, int32(weapon.Level))
	if weaponLevelConfig == nil {
		return config.CONFIG_NOT_FOUND
	}
	if weaponLevelConfig.Exp == 0 {
		logger.Error("weapon max level limit, weaponKey: %v, uid: %v", weaponKey, user.GetUid())
		return config.SERVER_ERROR
	}
	// 检查道具
	if req.ItemList != nil {
		ret := CheckSubItems(req.ItemList, user)
		if ret != config.OK {
			logger.Error("use item error code: %v, itemList: %v, uid: %v", ret, req.ItemList, user.GetUid())
			return int16(ret)
		}
	}
	// 检查武器
	foodWeaponList := make([]*sharemem.Weapon, 0)
	for _, foodWeaponKey := range req.FoodWeaponList {
		user.GetEmptyWeapon()
		var foodWeapon = user.GetWeaponByKey(foodWeaponKey)
		if foodWeapon == nil {
			logger.Error("food weapon not exist, foodWeaponKey: %v, uid: %v", foodWeaponKey, user.GetUid())
			return config.OBJECT_NOT_FOUND
		}
		if foodWeapon.WeaponKey == weapon.WeaponKey {
			logger.Error("weapon can not eat self, foodWeaponKey: %v, uid: %v", foodWeaponKey, user.GetUid())
			return config.SERVER_ERROR
		}
		if foodWeapon.RoleId != 0 {
			logger.Error("weapon already be wear, foodWeaponKey: %v, roleId: %v, uid: %v", foodWeaponKey, foodWeapon.RoleId, user.GetUid())
			return config.SERVER_ERROR
		}
		foodWeaponList = append(foodWeaponList, foodWeapon)
	}
	// 计算道具的总经验
	totalAddExp := int32(0)
	for _, item := range req.ItemList {
		itemActiveConfig := gdconf.GetItemUseConfig(item.Id)
		if itemActiveConfig == nil {
			return config.CONFIG_NOT_FOUND
		}
		if itemActiveConfig.Param[0] != gdconf.ItemUseTypeAddGlobalExp {
			logger.Error("item type is not exp, type: %v, uid: %v", itemActiveConfig.Param[0], user.GetUid())
			return config.SERVER_ERROR
		}
		totalAddExp += itemActiveConfig.Param[1] * item.Num
	}
	// 计算食物武器的总经验
	weaponConvExpRatio := gdconf.GetConstFloat("WEAPONEXP")
	for _, foodWeapon := range foodWeaponList {
		foodWeaponConfig := gdconf.GetWeaponConfig(foodWeapon.ItemId)
		if foodWeaponConfig == nil {
			return config.CONFIG_NOT_FOUND
		}
		totalAddExp += foodWeaponConfig.BaseConvExp
		for level := int16(1); level < foodWeapon.Level; level++ {
			foodWeaponLevelConfig := gdconf.GetWeaponLevelConfigByGroupAndLevel(foodWeaponConfig.LevelGroup, int32(level))
			if foodWeaponLevelConfig == nil {
				return config.CONFIG_NOT_FOUND
			}
			totalAddExp += int32(float64(foodWeaponLevelConfig.Exp) * weaponConvExpRatio)
		}
		totalAddExp += int32(float64(foodWeapon.Exp) * weaponConvExpRatio)
	}
	// 武器增加经验
	newExp := weapon.Exp
	newLevel := weapon.Level
	newExp += totalAddExp
	var remainExp int32 = 0
	// 武器升级
	for i := 0; i < 1000; i++ {
		// 获取武器当前等级配置表
		weaponLevelConfig = gdconf.GetWeaponLevelConfigByGroupAndLevel(weaponConfig.LevelGroup, int32(newLevel))
		if weaponLevelConfig == nil {
			break
		}
		// 武器当前等级未突破则跳出循环
		if newLevel >= int16(weaponPromoteConfig.LevelLimit) {
			newExp = 0
			break
		}
		// 武器等级达到上限跳出循环
		if weaponLevelConfig.Exp == 0 {
			remainExp = newExp
			newExp = 0
			break
		}
		// 武器经验小于升级所需的经验则跳出循环
		if newExp < weaponLevelConfig.Exp {
			break
		}
		// 武器等级提升
		newExp -= weaponLevelConfig.Exp
		newLevel++
	}
	// 扣除道具
	SubItems(req.ItemList, user)
	goldExpRatio := gdconf.GetConstFloat("WEAPONUPGRADECOST")
	ok := CostItems(map[int32]int32{gdconf.ITEM_GOLD: int32(float32(goldExpRatio) * float32(math.Max(float64(totalAddExp-remainExp), 0)))}, user)
	if !ok {
		return config.ITEM_NOT_ENOUGH
	}
	// 扣除武器
	for _, foodWeapon := range foodWeaponList {
		ok = CostWeapon(foodWeapon, user)
		if !ok {
			logger.Error("cost weapon error, WeaponKey: %v, uid: %v", foodWeapon.WeaponKey, user.GetUid())
			return config.SERVER_ERROR
		}
	}
	// 修改数据
	var oldValue = weapon.Level
	weapon.Level = newLevel
	weapon.Exp = newExp
	if oldValue != weapon.Level {
		TriggerQuest(user, sharemem.QUEST_FINISH_COND_TYPE_WEAPON_COUNT_BY_LEVEL, true)
	}
	logger.Debug("weapon upgrade ok, weapon: %v, uid: %v", weapon, user.GetUid())
	return config.OK
}

func WeaponPromote(weapon *sharemem.Weapon, req *pb.CS_WeaponPromoteReq, user *sharemem.User) int16 {
	// 检查配置
	weaponConfig := gdconf.GetWeaponConfig(weapon.ItemId)
	if weaponConfig == nil {
		return config.CONFIG_NOT_FOUND
	}
	weaponPromoteConfig := gdconf.GetWeaponPromoteConfigByGroupAndPromote(weaponConfig.PromoteGroup, int32(weapon.Promote))
	if weaponPromoteConfig == nil {
		return config.CONFIG_NOT_FOUND
	}
	// 武器等级是否满足
	if weapon.Level != int16(weaponPromoteConfig.LevelLimit) {
		logger.Error("weapon level != level limit, level: %v, uid: %v", weapon.Level, user.GetUid())
		return config.SERVER_ERROR
	}
	// 是否达到最大等阶
	if gdconf.GetWeaponPromoteConfigByGroupAndPromote(weaponConfig.PromoteGroup, int32(weapon.Promote+1)) == nil {
		logger.Error("weapon max promote limit, promote: %v, uid: %v", weapon.Promote, user.GetUid())
		return config.SERVER_ERROR
	}
	// 玩家等级是否满足
	if user.Level < int16(weaponPromoteConfig.NeedPlayerLevel) {
		logger.Error("player level < need player level, level: %v, uid: %v", user.Level, user.GetUid())
		return config.SERVER_ERROR
	}
	// 校验并扣除道具
	for _, material := range weaponPromoteConfig.CostItemList {
		if !slices.ContainsFunc[[]*pb.Item_Req, *pb.Item_Req](req.ItemList, func(item *pb.Item_Req) bool {
			return item.Id == material.ItemId && item.Num == material.ItemCount
		}) {
			logger.Error("client req item not match with config, uid: %v", user.GetUid())
			return config.SERVER_ERROR
		}
	}
	SubItems(req.ItemList, user)
	ok := CostItems(map[int32]int32{gdconf.ITEM_GOLD: weaponPromoteConfig.Gold}, user)
	if !ok {
		return config.ITEM_NOT_ENOUGH
	}
	// 武器升阶
	weapon.Promote++
	return config.OK
}

func WeaponStar(weapon *sharemem.Weapon, req *pb.CS_WeaponStarReq, user *sharemem.User) int16 {
	// 检查配置
	weaponConfig := gdconf.GetWeaponConfig(weapon.ItemId)
	if weaponConfig == nil {
		return config.CONFIG_NOT_FOUND
	}
	weaponStarConfig := gdconf.GetWeaponStarConfigByGroupAndStar(weaponConfig.StarGroup, int32(weapon.Star+1))
	if weaponStarConfig == nil {
		return config.CONFIG_NOT_FOUND
	}
	// 校验并扣除武器
	for _, foodWeaponKey := range req.FoodWeaponList {
		var foodWeapon = user.GetWeaponByKey(foodWeaponKey)
		if foodWeapon == nil {
			logger.Error("get foodWeapon is nil, foodWeaponKey: %v, uid: %v", foodWeaponKey, user.GetUid())
			continue
		}
		if foodWeapon.ItemId != weapon.ItemId {
			logger.Error("food weapon item id not match with target weapon, food: %v, target: %v, uid: %v", foodWeapon.ItemId, weapon.ItemId, user.GetUid())
			continue
		}
		star := foodWeapon.Star
		ok := CostWeapon(foodWeapon, user)
		if !ok {
			logger.Error("cost weapon fail, foodWeapon: %v, uid: %v", foodWeapon, user.GetUid())
			continue
		}
		weapon.Star += star + 1
	}
	maxStar := int8(0)
	for i := 0; i < 100; i++ {
		weaponStarConfig = gdconf.GetWeaponStarConfigByGroupAndStar(weaponConfig.StarGroup, int32(i))
		if weaponStarConfig == nil {
			break
		}
		maxStar = int8(i)
	}
	if weapon.Star > maxStar {
		weapon.Star = maxStar
	}
	return config.OK
}

//=========消息协议处理=================

func WeaponWearReq(user *sharemem.User, buf *mysock.SockBuf) int16 {
	req, ok := ParseMsg(config.CS_WeaponWearReq, new(pb.CS_WeaponWearReq), buf, user).(*pb.CS_WeaponWearReq)
	if !ok {
		return config.PROTO_ERROR
	}

	weapon := user.GetWeaponByKey(req.WeaponKey)
	if weapon == nil {
		logger.Error("weapon not exist, WeaponKey: %v, uid: %v", req.WeaponKey, user.GetUid())
		return SendMsg(config.SC_WeaponWearRsp, &pb.SC_WeaponWearRsp{RetCode: config.OBJECT_NOT_FOUND}, buf, user)
	}
	role := user.GetRoleById(req.RoleId)
	if role == nil {
		logger.Error("role not exist, roleId: %v, uid: %v", req.RoleId, user.GetUid())
		return SendMsg(config.SC_WeaponWearRsp, &pb.SC_WeaponWearRsp{RetCode: config.OBJECT_NOT_FOUND}, buf, user)
	}
	ret := WeaponWear(weapon, role, req.IsWear, user)
	if ret != config.OK {
		logger.Error("weapon wear fail, weapon: %v, uid: %v", weapon, user.GetUid())
		return SendMsg(config.SC_WeaponWearRsp, &pb.SC_WeaponWearRsp{RetCode: int32(ret)}, buf, user)
	}

	return SendMsg(config.SC_WeaponWearRsp, &pb.SC_WeaponWearRsp{RetCode: config.OK, Weapon: user.PacketWeapon(weapon), Role: user.PacketUserRole(role)}, buf, user)
}

func WeaponUpgradeReq(user *sharemem.User, buf *mysock.SockBuf) int16 {
	req, ok := ParseMsg(config.CS_WeaponUpgradeReq, new(pb.CS_WeaponUpgradeReq), buf, user).(*pb.CS_WeaponUpgradeReq)
	if !ok {
		return config.PROTO_ERROR
	}

	weapon := user.GetWeaponByKey(req.WeaponKey)
	if weapon == nil {
		logger.Error("weapon not exist, WeaponKey: %v, uid: %v", req.WeaponKey, user.GetUid())
		return SendMsg(config.SC_WeaponUpgradeRsp, &pb.SC_WeaponUpgradeRsp{RetCode: config.OBJECT_NOT_FOUND}, buf, user)
	}
	ret := WeaponUpgrade(weapon, req, user)
	if ret != config.OK {
		logger.Error("weapon upgrade fail, weapon: %v, uid: %v", weapon, user.GetUid())
		return SendMsg(config.SC_WeaponUpgradeRsp, &pb.SC_WeaponUpgradeRsp{RetCode: int32(ret)}, buf, user)
	}

	SendMsg(config.SC_WeaponUpgradeRsp, &pb.SC_WeaponUpgradeRsp{RetCode: config.OK, Weapon: user.PacketWeapon(weapon)}, buf, user)

	if weapon.RoleId != 0 {
		role := user.GetRoleById(weapon.RoleId)
		if role == nil {
			logger.Error("role not exist, roleId: %v, uid: %v", weapon.RoleId, user.GetUid())
			return SendMsg(config.SC_WeaponUpgradeRsp, &pb.SC_WeaponUpgradeRsp{RetCode: config.OBJECT_NOT_FOUND}, buf, user)
		}
		SendMsg(config.SC_RoleUpdateNtf, &pb.SC_RoleUpdateNtf{Role: user.PacketUserRole(role)}, buf, user)
	}

	return config.OK
}

func WeaponPromoteReq(user *sharemem.User, buf *mysock.SockBuf) int16 {
	req, ok := ParseMsg(config.CS_WeaponPromoteReq, new(pb.CS_WeaponPromoteReq), buf, user).(*pb.CS_WeaponPromoteReq)
	if !ok {
		return config.PROTO_ERROR
	}

	weapon := user.GetWeaponByKey(req.WeaponKey)
	if weapon == nil {
		logger.Error("weapon not exist, WeaponKey: %v, uid: %v", req.WeaponKey, user.GetUid())
		return SendMsg(config.SC_WeaponPromoteRsp, &pb.SC_WeaponPromoteRsp{RetCode: config.OBJECT_NOT_FOUND}, buf, user)
	}
	ret := WeaponPromote(weapon, req, user)
	if ret != config.OK {
		logger.Error("weapon promote fail, weapon: %v, uid: %v", weapon, user.GetUid())
		return SendMsg(config.SC_WeaponPromoteRsp, &pb.SC_WeaponPromoteRsp{RetCode: int32(ret)}, buf, user)
	}

	SendMsg(config.SC_WeaponPromoteRsp, &pb.SC_WeaponPromoteRsp{RetCode: config.OK, Weapon: user.PacketWeapon(weapon)}, buf, user)

	if weapon.RoleId != 0 {
		role := user.GetRoleById(weapon.RoleId)
		if role == nil {
			logger.Error("role not exist, roleId: %v, uid: %v", weapon.RoleId, user.GetUid())
			return SendMsg(config.SC_WeaponPromoteRsp, &pb.SC_WeaponPromoteRsp{RetCode: config.OBJECT_NOT_FOUND}, buf, user)
		}
		SendMsg(config.SC_RoleUpdateNtf, &pb.SC_RoleUpdateNtf{Role: user.PacketUserRole(role)}, buf, user)
	}

	return config.OK
}

func WeaponStarReq(user *sharemem.User, buf *mysock.SockBuf) int16 {
	req, ok := ParseMsg(config.CS_WeaponStarReq, new(pb.CS_WeaponStarReq), buf, user).(*pb.CS_WeaponStarReq)
	if !ok {
		return config.PROTO_ERROR
	}

	weapon := user.GetWeaponByKey(req.WeaponKey)
	if weapon == nil {
		logger.Error("weapon not exist, WeaponKey: %v, uid: %v", req.WeaponKey, user.GetUid())
		return SendMsg(config.SC_WeaponStarRsp, &pb.SC_WeaponStarRsp{RetCode: config.OBJECT_NOT_FOUND}, buf, user)
	}
	ret := WeaponStar(weapon, req, user)
	if ret != config.OK {
		logger.Error("weapon star fail, weapon: %v, uid: %v", weapon, user.GetUid())
		return SendMsg(config.SC_WeaponStarRsp, &pb.SC_WeaponStarRsp{RetCode: int32(ret)}, buf, user)
	}

	SendMsg(config.SC_WeaponStarRsp, &pb.SC_WeaponStarRsp{RetCode: config.OK, Weapon: user.PacketWeapon(weapon)}, buf, user)

	if weapon.RoleId != 0 {
		role := user.GetRoleById(weapon.RoleId)
		if role == nil {
			logger.Error("role not exist, roleId: %v, uid: %v", weapon.RoleId, user.GetUid())
			return SendMsg(config.SC_WeaponStarRsp, &pb.SC_WeaponStarRsp{RetCode: config.OBJECT_NOT_FOUND}, buf, user)
		}
		SendMsg(config.SC_RoleUpdateNtf, &pb.SC_RoleUpdateNtf{Role: user.PacketUserRole(role)}, buf, user)
	}

	return config.OK
}

func WeaponLockReq(user *sharemem.User, buf *mysock.SockBuf) int16 {
	req, ok := ParseMsg(config.CS_WeaponLockReq, new(pb.CS_WeaponLockReq), buf, user).(*pb.CS_WeaponLockReq)
	if !ok {
		return config.PROTO_ERROR
	}

	weapon := user.GetWeaponByKey(req.WeaponKey)
	if weapon == nil {
		logger.Error("weapon not exist, WeaponKey: %v, uid: %v", req.WeaponKey, user.GetUid())
		return SendMsg(config.SC_WeaponLockRsp, &pb.SC_WeaponLockRsp{RetCode: config.OBJECT_NOT_FOUND}, buf, user)
	}
	weapon.IsLock = req.IsLock

	SendMsg(config.SC_WeaponLockRsp, &pb.SC_WeaponLockRsp{RetCode: config.OK, Weapon: user.PacketWeapon(weapon)}, buf, user)

	if weapon.RoleId != 0 {
		role := user.GetRoleById(weapon.RoleId)
		if role == nil {
			logger.Error("role not exist, roleId: %v, uid: %v", weapon.RoleId, user.GetUid())
			return SendMsg(config.SC_WeaponStarRsp, &pb.SC_WeaponStarRsp{RetCode: config.OBJECT_NOT_FOUND}, buf, user)
		}
		SendMsg(config.SC_RoleUpdateNtf, &pb.SC_RoleUpdateNtf{Role: user.PacketUserRole(role)}, buf, user)
	}

	return config.OK
}
